home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
day.arc
/
DAYLIB.PLB
< prev
Wrap
Text File
|
1985-08-24
|
9KB
|
219 lines
{ DAYLIB.PLB #3.00 85-08-17 TURBO PASCAL CALENDAR-DATE PROCEDURES
V03 L00 Turbo Pascal version cloned on 85-08-17 by Dennis
E. Hamilton. This version has smoother operation than
was available in the Small-C clone mother, DAYLIB.CL
version #2.04.
V02 L04 84-03-20 SC80 version updated by DEH to take advantage
of special fixed-point division entry in SC80.CC #5.06.
L00 derivation on 83-10-09 by DEH for use with SC80 Small-C.
V01 L00 created by DEH on 83-10-05 as DATES.PLB, for use with
JRT Pascal 3.0, switching ordinal range for use with DRI
software date stamps
V00 L00 original Pascal version created by Steve Brecher and
distributed on the CompuServe Programmers SIG by memo
dated 83-07-13.
These routines represent dates from January 1, 1978 to September 17, 2067
by the positive number of consecutive days since December 31, 1977. This is
the same ordinal day number used by Digital Research CP/M software for date
stamping. (The MSDOS ordinal date is obtained by a simple post-adjustment.
Negative ordinal date values are also allowed for use as pre-1978 dates,
being useful for computations using anniversaries and birth dates as far
back as Friday, April 13, 1888.)
Calendar dates of the form mo-day-year (with year in full 4-digit form)
are carried in structures of type calday. Simply declare that type for
your own variables, such as
var mmddyy: calday;
start: integer;
initializing the data as appropriate to your application. E.g.,
repeat
CalDate(mmddyy, Today);
(* establishing readln default values *)
write(CON, 'Start Date (mo day year)? ');
readln(CON, mmddyy.mo, mmddyy.da, mmddyy.year);
if mmddyy.year < 77
then mmddyy.year := mmddyy.year + 2000
else if mmddyy.year < 100
then mmddyy.year := mmddyy.year + 1900;
until not BadDate(mmddyy);
start := since77(mmddyy);
Ordinal dates are easier to store and manipulate within programs and data
wherever direct use by people can be avoided. Ordinal dates are perfect
for sorting by date, for comparison of two dates, and for determining the
exact number of days between any two dates. (These are so useful that
larger systems, such as Common LISP, carry ordinal SECONDS from New Year's
Eve, GMT, January 1, 1900. }
type
calday = record {Calendar date from 1978-01-01 to 2067-09-17. This
structure can be used for other dates on the Gregorian
calendar as well, but only the above are guaranteed to
have unambiguous, positive ordinal date values. With
the procedures here, dates 1888-04-13 to 1977-12-31 are
represented by ordinals < 1, but the nineteenth-century
dates are generally not usable if you translate to a
different origin (such as 1980-01-01 for MSDOS or the
turn of the century value for ArpaNet and Common LISP}
year: integer {typically 1900 .. 2067 with 0 for unknown
in some applications };
mo: byte { 1 .. 12 with 0 for unknown in some
applications. This sequence of fields
should be maintained for use in sorting
as typeless values when ordinal dates
aren't convenient for that purpose};
da: byte { 1 .. 31 with 0 for unknown in some
applications. Note that validity of
the precise combination of values is
very easily checked with the technique
in the BadDate function. }
end {calday};
function
WeekDay(day: integer {since77 ordinal date})
: integer {0 for Sunday, 1 for Monday, ..., 6 for Saturday};
begin {day-of-week for a specified since77 ordinal date}
while day < 1
do day := day+32767;
{Converting negative-range values properly}
WeekDay := pred(day) mod 7;
end {WeekDay};
function
since77(date: calday {to be converted} )
:integer {ordinal date: days that date is past 1977-12-31.
This CP/M time-stamp basis has 1888-04-13 = day -32768,
1977-12-31 = day 0
2067-09-17 = day +32767 };
const
CumDays: array [0 .. 24] of integer
{days prior to first day of month for normal and leap years}
= (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 );
var day: integer {intermediate accumulation};
begin {since77}
day := (date.year - 1978)*365 + ord(date.year < 1901);
{determining the number of non-leap days from the end of date.year-1
to the end of 1977, pre-compensating for 1900 being no leap year}
if date.year < 1977
then day := day + (date.year - 1980) div 4
else day := day + (date.year - 1977) div 4;
{figuring in number of leap days also passed over in that interval}
if ((date.year and $3) = 0) and (date.year <> 1900)
then date.mo := date.mo + 12;
{adjusting for the current year itself being a leap-year (including 2000
but not 1900) by using month numbers 13-24 instead of 1-12}
since77 := day + CumDays[date.mo] + date.da;
{including the days prior to the requested day in the specified
year. This procedure lets da = 0 usefully compute the last day
of the preceding month, a handy variation. }
end {since77};
procedure
CalDate(var date: calday {returned as result};
day: integer {since77 ordinal date});
const
CumDays: array [0 .. 24] of integer
{days prior to first day of month for normal and leap years}
= (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 );
{an intentional duplication to keep this data private}
begin {CalDate conversion of a since77 ordinal date to its unique
corresponding Gregorian calendar date}
date.mo := 12; date.da := 31;
{setup to first compute the last day of year preceding that containing
the specified day number}
if day < 0
then date.year := Trunc((day-730.75-ord(day<-28488))/365.25) + 1978
{adjusting early-year determination for the 1900 leap skip too}
else date.year := Trunc((day+730.0)/365.25) + 1975;
{determining the PREVIOUS calendar year with careful adjustment for
leap days going into those previous years};
day := day - since77(date);
{accounting for days taken in preceding calendar years}
date.year := succ(date.year);
if ((date.year and $3) = 0) and (date.year <> 1900)
then date.mo := 24;
{using the leap-year block if needed. Otherwise,
start from date.mo = 12 already established}
while day <= CumDays[date.mo]
do date.mo := pred(date.mo);
{finding the month containing day in the current year}
date.da := day - CumDays[date.mo];
{figuring the number of days into the correct month}
if date.mo > 12
then date.mo := date.mo - 12;
{dropping the special leap-year month numbering}
end {CalDate};
function
BadDate(date: calday {mo-da-year to be checked})
:Boolean {for invalid calday, one not being for a unique
ordinal date in the supported range};
var check: calday {intermediate value for comparison};
begin {Screening of the given date for proper value};
CalDate(check, since77(date));
{finds the only supported date, if any, for the ordinal date produced by
since77 for the given date. The given date is valid here only if the
check value is identical. }
BadDate := (check.year <> date.year)
or (check.mo <> date.mo )
or (check.da <> date.da );
end {BadDate};
(* end of DAYLIB.PLB *)